/**
 * ReportGear(2011)
 */
package net.abbp.report.view.grid;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;

import net.abbp.report.utils.ReportUtils;
import net.abbp.report.view.GridView;

import com.reportgear.core.swing.pane.base.JBaseDialog;
import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.ColumnType;
import com.reportgear.report.view.cursor.CursorUtils;
import com.reportgear.report.view.grid.pane.NumberInputPane;
import com.reportgear.report.view.grid.selection.GridSelection;
import com.reportgear.report.view.grid.selection.Selection;
import com.reportgear.report.view.grid.support.DefaultGridColumnCRRenderer;
import com.reportgear.report.view.grid.support.GridCRRenderer;
import com.reportgear.report.view.grid.util.GraphHelper;

/**
 * Grid列头
 * <p>
 * 表格视图列头，对视图处理视图列,支持调整视图列的宽度,插入列等操作
 * 
 * @version 1.0 2011-3-14
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 * 
 */
public class GridColumnHeader extends BaseGridComponent {

    private static final long serialVersionUID = -8650563568761470347L;

    /**
     * 鼠标动作处理
     */
    private class GridColumnHeaderMouseHandler extends MouseInputAdapter {
        public int dragColumnIndex = 0;
        private JToolTip columnTip = null;
        private DragType dragType = DragType.DRAG_NONE;
        private int endMultiSelectIndex = 0;
        private boolean isDragPermited = false;
        private boolean isMulitColumnsSelectDragPermited = false;
        private int startMultiSelectIndex = 0;
        private JWindow tipWindow = null;

        public GridColumnHeaderMouseHandler() {
            this.isMulitColumnsSelectDragPermited = false;
            this.startMultiSelectIndex = 0;
            this.endMultiSelectIndex = 0;
            this.isDragPermited = false;
            this.dragColumnIndex = 0;
            this.columnTip = null;
            this.tipWindow = null;
        }

        public String createToolTipString(double d, double d1) {
            return "宽度: " + d + "/" + d1 + "像素";
        }

        private String createToolTipString(int i) {
            return i + "C";
        }

        /**
         * 选择列
         * 
         * @param column 列
         */
        private void doSelectColumn(int column) {
            final GridSelection gridSelection = gridView.getGridSelection();
            gridSelection.setType(GridSelection.Type.CELL);
            this.startMultiSelectIndex = this.endMultiSelectIndex = column;

            gridSelection.setEditBounds(column, 0, 1, 1);
            gridSelection.setOnlySelection(column, 0, 1, gridView.getRowCount());

            gridView.refresh();
            gridView.fireGridSelectionChanged();
        }

        private void hideToolTip() {
            if (this.tipWindow != null) {
                this.tipWindow.setVisible(false);
            }
        }

        private boolean isOnNormalSeparatorLine(double value, double refValue) {
            return Math.abs(value - refValue) < 2D;
        }

        @Override
        public void mouseDragged(MouseEvent evt) {
            if (!isEnabled()) {
                return;
            }

            if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
                return;
            }

            gridView.stopEditing();

            GridSelection gridSelection = gridView.getGridSelection();
            gridSelection.setType(GridSelection.Type.CELL);
            int xPos = evt.getX();
            int yPos = evt.getY();

            if (this.dragType == DragType.DRAG_SELECTED_COLUMNS) {// 多列选择
                if (!this.isMulitColumnsSelectDragPermited) {
                    return;
                }

                final ColumnRow columnrow = ReportUtils.getColumnRowByXY(gridView, xPos, yPos);

                this.endMultiSelectIndex = columnrow.getColumn();
                final int colSpan = Math.abs(this.endMultiSelectIndex - this.startMultiSelectIndex) + 1;
                final int startIndex = Math.min(this.startMultiSelectIndex, this.endMultiSelectIndex);

                gridSelection.setEditBounds(startIndex, 0, 1, 1);
                gridSelection.setOnlySelection(startIndex, 0, colSpan, gridView.getRowCount());

                this.setToolTipText2(this.createToolTipString(colSpan));

                gridView.fireGridSelectionChanged();
            } else {// 单行选择
                if (!this.isDragPermited) {
                    return;
                }

                final ColumnPropList cpl = reportModel.getColumnPropList();

                final int w = xPos - cpl.getRangeValue(gridView.getHoriBeginValue(), this.dragColumnIndex);
                cpl.setWidth(this.dragColumnIndex, w);

                this.setToolTipText2(this.createToolTipString(cpl.getWidth(this.dragColumnIndex),
                                                              cpl.getRangeValue(0, this.dragColumnIndex + 1)));
            }

            gridView.refresh();
        }

        @Override
        public void mouseMoved(MouseEvent evt) {
            if (!isEnabled()) {
                return;
            }

            setCursor(CursorUtils.CURSOR_H_SELECT);
            double xPos = evt.getX();
            ColumnPropList cpl = reportModel.getColumnPropList();

            double tmpWidth = 0.0D;
            double colWidth = 0.0D;
            double tmpTotalWidth = 0.0D;

            double width = getSize().getWidth();
            int pointer = gridView.getHoriBeginValue();
            int maxColumn = gridView.getMaxColumnCount();
            while ((tmpTotalWidth < width) && (pointer < maxColumn)) {
                tmpWidth += colWidth;
                colWidth = cpl.getWidth(pointer);

                if (colWidth <= 0.0D) {
                    tmpTotalWidth = tmpWidth + 1.0D;
                } else {
                    tmpTotalWidth = tmpWidth + colWidth;
                }

                if (this.isOnNormalSeparatorLine(xPos, tmpTotalWidth)) {
                    setCursor(CursorUtils.CURSOR_H_MOVE);
                    return;
                }

                pointer++;
            }

        }

        @Override
        public void mousePressed(MouseEvent evt) {

            if (!isEnabled()) {
                return;
            }

            gridView.stopEditing();

            int xPos = evt.getX();
            int yPos = evt.getY();
            boolean isRightButton = SwingUtilities.isRightMouseButton(evt);
            ColumnPropList cpl = reportModel.getColumnPropList();

            double tmpWidth = 0.0D;
            double columnWidth = 0.0D;
            double tmpTotalWidth = 0.0D;
            double width = getSize().getWidth();

            int pointer = gridView.getHoriBeginValue();
            while (tmpTotalWidth < width) {
                tmpWidth += columnWidth;
                columnWidth = cpl.getWidth(pointer);

                if (columnWidth <= 0.0D) {
                    tmpTotalWidth = tmpWidth + 1.0D;
                } else {
                    tmpTotalWidth = tmpWidth + columnWidth;
                }

                if (this.isOnNormalSeparatorLine(xPos, tmpTotalWidth)) {
                    this.dragType = DragType.DRAG_WIDTH;
                    this.isDragPermited = true;
                    this.dragColumnIndex = pointer;
                    final MouseEvent event = evt;
                    final ColumnPropList pl = cpl;
                    SwingUtilities.invokeLater(new Runnable() {

                        public void run() {
                            showToolTip(event,
                                        createToolTipString(pl.getWidth(dragColumnIndex),
                                                            pl.getRangeValue(0, dragColumnIndex + 1)));
                        }
                    });

                    break;
                }

                if (xPos > tmpWidth && xPos <= tmpTotalWidth) {
                    this.dragType = DragType.DRAG_SELECTED_COLUMNS;
                    this.isMulitColumnsSelectDragPermited = true;
                    this.startMultiSelectIndex = pointer;
                    this.showToolTip(evt, this.createToolTipString(this.startMultiSelectIndex + 1));
                    break;
                }

                pointer++;
            }

            if (evt.getClickCount() > 1) {
                if (popupMenu == null) {
                    popupMenu = new GridColumnPopupMenu();
                }
                this.hideToolTip();
            }
            if (isRightButton) {
                if (popupMenu == null) {
                    popupMenu = new GridColumnPopupMenu();
                }
                this.hideToolTip();
                popupMenu.show(GridColumnHeader.this, xPos, yPos);
            } else if (this.dragType == DragType.DRAG_SELECTED_COLUMNS) {
                gridView.getGridSelection().setType(GridSelection.Type.CELL);

                ColumnRow columnRow = ReportUtils.getColumnRowByXY(gridView, xPos, yPos);
                this.doSelectColumn(columnRow.getColumn());
            }

            gridView.refresh();
        }

        @Override
        public void mouseReleased(MouseEvent evt) {
            if (!isEnabled()) {
                return;
            }

            if (this.dragType == DragType.DRAG_NONE || SwingUtilities.isRightMouseButton(evt)) {
                return;
            }

            gridView.stopEditing();
            this.hideToolTip();
            GridSelection gridSelection = gridView.getGridSelection();
            Selection rect = gridSelection.getLastCellSelection();
            ColumnPropList cpl = reportModel.getColumnPropList();

            if (this.dragType == DragType.DRAG_WIDTH) {
                int col = rect.getColumn();
                int w = rect.getColumSpan();

                if ((this.dragColumnIndex - col < w) && (this.dragColumnIndex >= col)) {
                    for (int i = 0; i < w; i++) {
                        cpl.setWidth(col + i, cpl.getWidth(this.dragColumnIndex));
                    }
                }
                gridView.fireReportDataChanged();
            }

            this.isMulitColumnsSelectDragPermited = false;
            this.isDragPermited = false;
            this.dragType = DragType.DRAG_NONE;
            this.dragColumnIndex = 0;

            gridView.repaint();
        }

        private void setToolTipText2(String s) {
            if (this.columnTip == null) {
                return;
            } else {
                this.columnTip.setTipText(s);
                this.columnTip.setSize(this.columnTip.getPreferredSize());
                this.tipWindow.pack();
                this.tipWindow.repaint();
                return;
            }
        }

        public void showToolTip(MouseEvent evt, String s) {
            if (this.tipWindow == null) {
                this.tipWindow = new JWindow();
                this.columnTip = createToolTip();
                this.tipWindow.add(this.columnTip);
            }
            this.columnTip.setTipText(s);
            Point point = new Point(evt.getX(), 0);

            SwingUtilities.convertPointToScreen(point, GridColumnHeader.this);
            Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
            Dimension dimension1 = this.columnTip.getPreferredSize();
            int i = Math.max(0, Math.min(point.x - dimension1.width / 2, dimension.width - dimension1.width));
            int j = point.y - dimension1.height - 2;

            this.tipWindow.setLocation(i, j);
            this.tipWindow.pack();
            this.tipWindow.setVisible(true);
        }
    }

    private class GridColumnPopupMenu extends JPopupMenu {
        private static final long serialVersionUID = -1636732277741853439L;

        public GridColumnPopupMenu() {
//            this.add(new JMenuItem(new AddColumnAction()));
//            this.add(new JMenuItem(new AddColumn2Action()));
//            this.add(new JMenuItem(new DeleteColumnAction()));
//            this.addSeparator();
            this.add(new JMenuItem(new SetColumnWidthAction()));
            this.addSeparator();
            this.add(new JMenuItem(new SetRepeatColumnHeaderAction()));
            // this.add(new JMenuItem(new SetRepeatColumnFooterAction()));
            this.add(new JMenuItem(new CancelRepeatRowAction()));
        }

//        private class AddColumnAction extends AbstractAction {
//
//            /**
//             * UID
//             */
//            private static final long serialVersionUID = 2715599679544981017L;
//
//            public AddColumnAction() {
//                super("插入列(之前)");
//            }
//
//            public void actionPerformed(ActionEvent e) {
//                Selection selection = gridView.getLastCellSelection();
//
//                gridView.getReportModel().addColumn(selection.getColumn());
//                gridView.refresh();
//            }
//        }
//
//        private class AddColumn2Action extends AbstractAction {
//
//            /**
//             * UID
//             */
//            private static final long serialVersionUID = -460121719538233986L;
//
//            public AddColumn2Action() {
//                super("插入列(之后)");
//            }
//
//            public void actionPerformed(ActionEvent e) {
//                Selection selection = gridView.getLastCellSelection();
//
//                gridView.getReportModel().addColumn(selection.getColumn() + 1);
//                gridView.refresh();
//            }
//        }
//
//        /**
//         * 删除行
//         */
//        private class DeleteColumnAction extends AbstractAction {
//
//            private static final long serialVersionUID = 6489481371083094876L;
//
//            public DeleteColumnAction() {
//                super("删除列");
//            }
//
//            public void actionPerformed(ActionEvent e) {
//                Selection selection = gridView.getLastCellSelection();
//
//                int col = selection.getColumn();
//                int colCount = selection.getColumSpan();
//
//                for (int i = colCount - 1; i >= 0; i--) {
//                    reportModel.removeColumn(col + i);
//                }
//
//                gridView.refresh();
//            }
//        }

        private class SetColumnWidthAction extends AbstractAction {

            private static final long serialVersionUID = -6536134780439341544L;

            public SetColumnWidthAction() {
                super("设置列宽");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int col = selection.getColumn();
                int colCount = selection.getColumSpan();

                NumberInputPane inputPane = new NumberInputPane();
                inputPane.setTitle("宽度");
                inputPane.populate(reportModel.getColumnWidth(col));

                if (inputPane.showDialog(null) == JBaseDialog.RESULT_OK) {
                    int value = (int) inputPane.update();
                    for (int i = 0; i < colCount; i++) {
                        reportModel.setColumnWidth(col + i, value);
                    }

                    gridView.refresh();
                }
            }
        }

        private class SetRepeatColumnHeaderAction extends AbstractAction {

            private static final long serialVersionUID = -3510451591553747406L;

            public SetRepeatColumnHeaderAction() {
                super("设置固定列");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int col = selection.getColumn();
                int colCount = selection.getColumSpan();

                for (int i = 0; i < colCount; i++) {
                    int index = col + i;

                    ColumnType type = reportModel.getColumnType(index);

                    reportModel.setColumnType(index, ColumnType.HEADER);
                }

                gridView.refresh();
            }
        }

//        private class SetRepeatColumnFooterAction extends AbstractAction {
//
//            private static final long serialVersionUID = -1345325482848179143L;
//
//            public SetRepeatColumnFooterAction() {
//                super("设置重复结尾列");
//            }
//
//            public void actionPerformed(ActionEvent e) {
//                Selection selection = gridView.getLastCellSelection();
//
//                int col = selection.getColumn();
//                int colCount = selection.getColumSpan();
//
//                for (int i = 0; i < colCount; i++) {
//                    int index = col + i;
//
//                    ColumnType type = reportModel.getColumnType(index);
//
//                    reportModel.setColumnType(index, ColumnType.FOOTER);
//                }
//
//                gridView.refresh();
//            }
//        }

        private class CancelRepeatRowAction extends AbstractAction {

            private static final long serialVersionUID = -3377144053664393548L;

            public CancelRepeatRowAction() {
                super("取消固定列");
            }

            public void actionPerformed(ActionEvent e) {
                Selection selection = gridView.getLastCellSelection();

                int col = selection.getColumn();
                int colCount = selection.getColumSpan();

                for (int i = 0; i < colCount; i++) {
                    int index = col + i;

                    reportModel.setColumnType(index, ColumnType.NORMAL);
                }

                gridView.refresh();
            }
        }
    }

    // 单元格Render
    private GridCRRenderer gridCRRender;
    // 弹出菜单
    public GridColumnPopupMenu popupMenu;
    // 选中背景颜色
    private Color selectedBackground = new Color(253, 216, 153);
    // 选中前景颜色
    private Color selectedForeground = Color.BLACK;

    // 分隔线颜色
    private Color separatorLineColor = new Color(172, 168, 153);

    // 临时矩形对象
    private Rectangle2D tmpRectangle2D = new Rectangle2D.Double(0, 0, 0, 0);

    public GridColumnHeader(GridView workSheet) {
        super(workSheet);

        final GridColumnHeaderMouseHandler mouseHandler = new GridColumnHeaderMouseHandler();
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
        this.gridCRRender = new DefaultGridColumnCRRenderer(this.reportModel);
        this.getInputMap().clear();
        this.getActionMap().clear();
        this.setFocusable(false);
        this.setOpaque(true);
    }

    /**
     * 获取标题绘制对象
     * 
     * @return 标题绘制对象
     */
    public GridCRRenderer getGridCRRender() {
        return this.gridCRRender;
    }

    @Override
    public Dimension getPreferredSize() {
        if (this.gridView.isColumnHeaderVisible()) {
            return new Dimension(super.getPreferredSize().width, this.getFontMetrics(this.getFont()).getHeight() + 4);
        } else {
            return new Dimension(0, 0);
        }
    }

    /**
     * 设置选择背景色
     * 
     * @return 选择背景色
     */
    public Color getSelectedBackground() {
        return this.selectedBackground;
    }

    /**
     * 获取选择前景色
     * 
     * @return 选择前景色
     */
    public Color getSelectedForeground() {
        return this.selectedForeground;
    }

    /**
     * 获取分割线颜色
     * 
     * @return 分割线颜色
     */
    public Color getSeparatorLineColor() {
        return this.separatorLineColor;
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Dimension dim = this.getSize();

        // 绘制背景
        this.tmpRectangle2D.setRect(0.0D, 0.0D, dim.getWidth(), dim.getHeight());
        g2.setPaint(this.getBackground());
        GraphHelper.fill(g2, this.tmpRectangle2D);

        FontRenderContext frContext = g2.getFontRenderContext();
        LineMetrics lineMetrics = this.getFont().getLineMetrics("A", frContext);
        float ascent = lineMetrics.getAscent();

        // 绘制分隔线
        g2.setPaint(this.getSeparatorLineColor());
        GraphHelper.drawLine(g2, 0.0D, 0.0D, 0.0D, dim.getHeight());

        GridSelection gridSelection = this.gridView.getGridSelection();

        ColumnPropList cpl = this.reportModel.getColumnPropList();
        double clientWidth = dim.getWidth();
        int beginIndex = this.gridView.getHoriBeginValue();
        int colExtend = 0;
        double tmpWidth1 = 0.0D;
        double tmpWidth2 = 0.0D;
        double columnWidth = 0.0D;

        final List<Integer> selectedIndexList = ReportUtils.getGridSelectionColumnIndex(gridSelection);
        boolean tmpSelectStatus;
        int columnCount = this.gridView.getColumnCount();
        while ((tmpWidth2 < clientWidth) && (beginIndex < columnCount)) {
            tmpSelectStatus = false;
            tmpWidth1 += columnWidth;
            columnWidth = cpl.getWidth(beginIndex);

            if (columnWidth <= 0.0D) {
                tmpWidth2 = tmpWidth1 + 1.0D;
            } else {
                tmpWidth2 = tmpWidth1 + columnWidth;
            }

            if (selectedIndexList.contains(beginIndex)) {
                g2.setColor(this.getSelectedBackground());
                GraphHelper.fill(g2, new Rectangle2D.Double(tmpWidth1 + 1.0D, 0.0D, columnWidth - 1.0D, dim.height));
                tmpSelectStatus = true;
            } else {
                tmpSelectStatus = false;
            }

            g2.setColor(this.getSeparatorLineColor());
            GraphHelper.drawLine(g2, tmpWidth2, 0.0D, tmpWidth2, dim.getHeight());
            final Object render = this.getGridCRRender().getDisplayInfo(beginIndex);

            if ((render instanceof String) || (render instanceof Number)) {
                final String str = render.toString() + cpl.getTypeStringShort(beginIndex);
                final double strWidth = this.getFont().getStringBounds(str, frContext).getWidth();

                if (tmpSelectStatus) {
                    g2.setPaint(this.getSelectedForeground());
                } else if (this.isEnabled()) {
                    g2.setPaint(this.getForeground());
                } else {
                    g2.setPaint(UIManager.getColor("controlShadow"));
                }
                GraphHelper.drawString(g2, str, tmpWidth1 + (columnWidth - strWidth) / 2.0D, ascent + 2.0F + 1.0F);
            }

            beginIndex++;
            colExtend++;
        }
        // 设置视图显示列
        this.gridView.setHoriValueExtend(colExtend - 1);

        g2.setColor(this.getSeparatorLineColor());
        GraphHelper.drawLine(g2, 0.0D, 0.0D, clientWidth, 0.0D);
    }

    /**
     * 设置标题绘制对象
     * 
     * @param render 绘制对象
     */
    public void setGridCRRender(GridCRRenderer render) {
        this.gridCRRender = render;
    }

    /**
     * 设置选择背景色
     * 
     * @param newColor 新颜色
     */
    public void setSelectedBackground(Color newColor) {
        this.selectedBackground = newColor;
        this.repaint();
    }

    /**
     * 设置选择前景色
     * 
     * @param newColor 新颜色
     */
    public void setSelectedForeground(Color newColor) {
        this.selectedForeground = newColor;
        this.repaint();
    }

    /**
     * 设置分隔线颜色
     * 
     * @param newColor 新颜色
     */
    public void setSeparatorLineColor(Color newColor) {
        this.separatorLineColor = newColor;
        this.repaint();
    }
}